/******************************************************************************
Copyright 2012 Google Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
******************************************************************************/

(function() {
  // A Google Charts API object displaying live voting results.
  var liveChart;
  // The Nth represents the state of the election after vote N.
  var dataTables = [];
  // Used to keep track of which element of the dataTables array is being requested, since it might not be available immediately.
  var queuedIndex = 0;
  // A Google Charts API object displaying a history of the voting. Not updated live.
  var historyChart;

  // This renders the history chart illustrating the specific voting breakdown after "index" votes have been cast.
  function updateHistoryChart(index) {
    index = parseInt(index);

    if (historyChart == null) {
      historyChart = new google.visualization.PieChart(document.getElementById("history-chart"));
    }

    if (dataTables[index] != null) {
      // If index corresponds to a state that has already been calculated by the Web Worker, display that state.
      queuedIndex = null;
      historyChart.draw(dataTables[index], {title: $.sprintf('Vote percentage after %d vote%s.', index + 1, index == 0 ? '' : 's')});
    } else {
      // Otherwise, make note of the fact that we want to display the state represented by index.
      // It will be displayed by the Worker.onMessage callback once it's been calculated.
      queuedIndex = index;
    }
  }

  // Creates a new Web Worker to calculate the voting breakdown states based on the raw data from App Engine.
  // Since an election might have an arbitrarily large number of votes, it's not as practical to calculate each state immediately.
  // The Web Worker runs in the background and doesn't affect the main UI.
  function initializeWebWorker(electionHistory, candidates) {
    var worker = new Worker('/static/javascript/view-web-worker.js');
    // This callback will be invoked by the worker code in view-web-worker.js
    $(worker).on('message', function(event) {
      // Read the data generated by the Web Worker.
      var data = event.originalEvent.data;

      // Stash away a dataTable reprsenting the election state for future display.
      dataTables[data.index] = new google.visualization.DataTable(data.dataTable);

      // After the first state has been calculated, activate the chart controls.
      if (data.index == 0) {
        $('#history-range').removeAttr('disabled');
        $('#play').removeAttr('disabled');
      }

      // If the UI has been waiting for this slice of the election history, update the chart display to show it.
      if (data.index == queuedIndex) {
        updateHistoryChart(queuedIndex);
      }
    });

    // Pass the Web Worker the info it needs to start its calculations.
    worker.postMessage({electionHistory: electionHistory, candidates: candidates});
  }

  // Animates the history chart by iterating through the available election history states.
  function play(index) {
    $('#history-range').val(index);

    updateHistoryChart(index);

    if (index < $('#history-range').attr('max')) {
      $('#play').attr('disabled', true);
      setTimeout(function() {
        play(index + 1);
      }, 1000);
    } else {
      $('#play').removeAttr('disabled');
    }
  }

  // This is called from the Channel connection handler when the page is notified about a new vote.
  // It's also called once from the main HTML's JavaScript section on page load, so it needs to be in the global namespace.
  window.updateLiveChart = function(electionState) {
    if (liveChart == null) {
      liveChart = new google.visualization.ColumnChart(document.getElementById("bar-chart"));
    }

    var numTotalVotes = 0;

    // Standard Google Charts API initialization.
    var data = new google.visualization.DataTable();
    data.addColumn('string', 'Candidate');
    data.addColumn('number', 'Votes');
    data.addRows(electionState.length);

    $.each(electionState, function(index, candidate) {
      data.setValue(index, 0, candidate.name);
      data.setValue(index, 1, candidate.votes);
      numTotalVotes += candidate.votes;
    });

    if (numTotalVotes > 0) {
      liveChart.draw(data, {colors: ["#caeefc"]});
    }
  }

  // Basic setup that needs to be done to initialize the history chart.
  // Since it's called from the HTML's JavaScript section it needs to be in the global namespace.
  window.initializeHistoryChart = function() {
    initializeWebWorker(window.electionHistory, window.candidates);

    $('#history-range').change(function() {
      updateHistoryChart($(this).val());
    });

    $('#play').click(function() {
      play(0);
    });
  }

  // Handles the client-side of the App Engine Channel API handshake, and defines callbacks.
  function openChannelConnection(electionId) {
    $.ajax({
      dataType: 'json',
      url: ($.sprintf('/elections/%s/generate_channel_token', encodeURIComponent(electionId))),
      success: function(json) {
        var channel = new goog.appengine.Channel(json.channelToken);
        var socket = channel.open();
        socket.onmessage = function(message) {
          window.updateLiveChart($.parseJSON(message.data));
        };
        socket.onerror = function(error) {
          console.log('Channel connection error:');
          console.dir(error);
        };
      }
    });
  }

  // Called once the DOM is ready.
  $(function() {
    if (window.electionId != null) {
      openChannelConnection(window.electionId);
    }

    if (window.countdownTime != null) {
      // This makes use of a jQuery countdown plugin to display the remaining time in an election.
      $('#countdown').countdown({
        until: new Date(window.countdownTime),
        description: window.countdownTimerText,
        onExpiry: function() {
          window.location.reload(true);
        }
      });
    }
  });
})();

